<?php

require_once('Classes/Xml/XmlParser.php');
require_once('Classes/Xml/XmlElement.php');

Mock::Generate( 'XmlElement' );

class Test_XmlParser extends UnitTestCase {

	function test_ANewlyCreatedXmlParserWillHaveAnEmptyElementStack() {
		$oXmlParser = new XmlParser();
		$this->assertIdentical( $oXmlParser->_GetStack(), array() );		
	}

	function test_CallingStartHandlerWillAddAnXmlElementOntoTheStackWithTheGivenNameAndAttributes() {
		
		// TODO: Could decompose the parser a little to allow for mocking in here...
		
		$oXmlParser = new XmlParser();
		$oXmlParser->StartHandler( null, 'ElementName', array( 'thing' => 'OtherThing' ) );

		$aStack = $oXmlParser->_GetStack();
		
		$this->assertIdentical( count( $aStack ), 1 );
		$this->assertTrue( is_a( $aStack[0], 'XmlElement') );
		$this->assertIdentical( $aStack[0]->GetName(), 'ELEMENTNAME' );
		$this->assertIdentical( $aStack[0]->GetAllAttributesAsAssociativeArray(), array( 'THING' => 'OtherThing' ) );
		
	}

	function test_CallingStartHandlerSeveralTimesWillAddAnXmlElementOntoTheStackForEachCallEvenIfAnElementIsRepeated() {

		$oXmlParser = new XmlParser();
		$oXmlParser->StartHandler( null, 'ElementName' , array( 'thing'  => 'OtherThing' ) );
		$oXmlParser->StartHandler( null, 'ElementName2', array( 'thing1' => 'OtherThing' ) );
		$oXmlParser->StartHandler( null, 'ElementName3', array( 'thing2' => 'OtherThing' ) );
		$oXmlParser->StartHandler( null, 'ElementName' , array( 'thing'  => 'OtherThing' ) );

		$aStack = $oXmlParser->_GetStack();
		
		$this->assertIdentical( count( $aStack ), 4 );
		foreach( $aStack as $oStackElement ) {
			$this->assertTrue( is_a( $oStackElement, 'XmlElement') );
		}
		
	}

	function test_CallingEndHandlerWhenTheStackIsEmptyWillReturnFalseAndNotMutateTheParser() {
		$oXmlParser = new XmlParser();
		$oOriginalXmlParser = $oXmlParser;

		$this->assertFalse( $oXmlParser->EndHandler( null, 'Name' ) );
		$this->assertIdentical( $oXmlParser, $oOriginalXmlParser );
	}
	
	function test_CallingEndHandlerWhenTheParserHasADocumentSetAndTheStackHasOneElementWillPopTheElementOffTheStackAndAddItAsAChildToTheXmlDocument() {

		$oXmlParser = new XmlParser();

		$oStackElement =& new MockXmlElement();
		
		$oXmlParser->_PushOntoStack( $oStackElement );
		
		$oDoc =& new MockXmlElement();
		$oDoc->expectOnce( 'AddElement', array( $oStackElement ) );
		
		$oXmlParser->_SetDocument( $oDoc );

		$this->assertTrue( $oXmlParser->EndHandler( null, 'Name' ) );

	}
	
	function test_CallingEndHandlerWhenTheParserHasADocumentSetAndTheTheStackHasMoreThanOneElementOnTheStackWillPopTheLastElementOffTheStackAndAddItToTheElementNextOnTheStack() {

		$oXmlParser = new XmlParser();

		$oParentStackElement  =& new MockXmlElement();
		$oChildElement        =& new MockXmlElement();

		$oParentStackElement->expectOnce( 'AddElement', array( $oChildElement ) );

		$oXmlParser->_PushOntoStack( $oParentStackElement );
		$oXmlParser->_PushOntoStack( $oChildElement );
		
		$oDoc =& new MockXmlElement();
		$oDoc->expectNever( 'AddElement' );
		
		$oXmlParser->_SetDocument( $oDoc );
		$oOriginalXmlParser = $oXmlParser;

		$this->assertTrue( $oXmlParser->EndHandler( null, 'Name' ) );

	}
	
	function test_CallingDataHandlerWhenTheParserStackHasMoreThanOneElementWillPopTheElementOffTheStackAndCallAddContentPassingTheDataBeforePushingItBackOnTheStack() {

		$oXmlParser = new XmlParser();

		$oTopStackElement  =& new MockXmlElement();
		$oTopStackElement->expectNever( 'SetContent' );
		$oXmlParser->_PushOntoStack( $oTopStackElement );

		$oBottomStackElement  =& new MockXmlElement();
		$oBottomStackElement->expectOnce( 'SetContent', array( '    Some text based data  ' ) );
		$oXmlParser->_PushOntoStack( $oBottomStackElement );

		$oXmlParser->DataHandler( null, '    Some text based data  ' );

		$this->assertIdentical( $oXmlParser->_GetNumberOfElementsOnStack(), 2 );
	}

	function test_CallingDataHandlerWithTheDataSetToAStringOfSpacesWillNotCallSetContentOnTheStackElement() {
		$oXmlParser = new XmlParser();

		$oTopStackElement  =& new MockXmlElement();
		$oTopStackElement->expectNever( 'SetContent' );
		$oXmlParser->_PushOntoStack( $oTopStackElement );

		$oBottomStackElement  =& new MockXmlElement();
		$oBottomStackElement->expectNever( 'SetContent' );
		$oXmlParser->_PushOntoStack( $oBottomStackElement );

		$oXmlParser->DataHandler( null, '      ' );

		$this->assertIdentical( $oXmlParser->_GetNumberOfElementsOnStack(), 2 );
		
	}

	function test_CallingDataHandlerWhenTheStackHasNoElementsAndNoDocumentIsSetWillReturnFalse() {
		$oXmlParser = new XmlParser();
		$this->assertFalse( $oXmlParser->DataHandler( null, 'This is a set of data' ) );
	}

	function test_CallingParseWithAnEmptyStringWillReturnFalse() {
		$oXmlParser = new XmlParser();
		$this->assertFalse( $oXmlParser->Parse( '' ) );	
	}
	
	function test_CallingParseWithAnInvalidXmlLikeStringWillReturnFalse() {
		$oXmlParser = new XmlParser();
		$this->assertFalse( $oXmlParser->Parse( '<xml>text<thing>more text<mismatched-tag></thing></xml>' ) );	
	}

	function test_CallingParseWithAValidXmlStringWillReturnAnXmlDocumentThatRepresentsTheStringPassedIn() {
		// TODO: The XmlDocument in here realy should be mocked... if this behaviour changes then replace with mock...
		$oXmlParser = new XmlParser();
		$oReturnedDoc = $oXmlParser->Parse( '<xml>text<thing>more text<tag-with-attributes attr1="VALUE" ATTRIB2="other val"/></thing></xml>' );	
		
		$this->assertTrue( is_a( $oReturnedDoc, 'XmlDocument') );
		$this->assertIdentical( $oReturnedDoc->GetName(), 'PARSEDDOCUMENT' );

		$sExpectedGenerate = "<?xml version=\"1.0\"?>\r\n\r\n<PARSEDDOCUMENT>\r\n  <XML>text\r\n    <THING>more text\r\n      <TAG-WITH-ATTRIBUTES attr1=\"VALUE\" attrib2=\"other val\" /></THING></XML>\r\n</PARSEDDOCUMENT>";

		$this->assertIdentical( $oReturnedDoc->Generate(), $sExpectedGenerate );
	}
}

?>
